#include "Sock.hpp"
#include <iostream>
#include <sys/select.h>
#include <string>

#define NUM (sizeof(fd_set) * 8)

void usage(string s)
{
    std::cout << s << " " << "port" << std::endl;
}

int main(int args, char *argv[])
{
    if (args != 2)
    {
        usage(argv[0]);
        exit(1);
    }

    // 1.创建listen套接字
    int listenfd = Socket::Sock();

    // 2.绑定套接字
    int16_t port = (uint16_t)atoi(argv[1]);
    Socket::Bind(listenfd, port);

    // 3.监听套接字
    Socket::Listen(listenfd);

    // 4.监听完套接字之后，不能立即accept
    // accept本质上也是从listenfd这个文件描述符中获取连接
    // 所以必须得等listenfd这个文件描述符就绪之后，accept再去获取连接
    // 那么listenfd什么时候就绪呢，三次握手建立连接之后就就绪了
    // 所以此时就得先让select帮忙等待listenfd就绪

    // 5.创建一个数组，保存要等待的文件描述符
    int fd_arry[NUM];

    // 6.数组的一个元素表示一个文件描述符
    // 元素值-1表示当前元素没有存储文件描述符
    // 元素值 等于0或者大于零 表示要等待的文件描述符的值
    // 先将数组的每个元素初始化为-1，即刚开始数组不存储任何文件描述符
    for (int i = 0; i < NUM; i++)
    {
        fd_arry[i] = -1;
    }

    // 7.将listenfd设置进fd_arry中，用第一个元素存储listen_fd
    fd_arry[0] = listenfd;

    for(;;)
    {
    // 8.设置select的第一个参数，最大的文件描述符
    int max_fd = listenfd;
    for (int i = 0; i < NUM; i++)
    {
        if (max_fd < fd_arry[i])
        {
            max_fd = fd_arry[i];
        }
    }

    // 9.创建readfds,并初始化readfds;
    fd_set readfds;
    FD_ZERO(&readfds);

    // 10.将要等待的文件描述符设置进readfds中
    for (int i = 0; i < NUM; i++)
    {
        if (fd_arry[i] != -1)
        {
            FD_SET(fd_arry[i], &readfds);
        }
    }

    // 11.调用select,让select等待位图中的文件描述符
    int n = select(max_fd+1, &readfds, NULL, NULL, NULL);

    switch (n)
    {
    case -1:
        std::cout << "select error" << std::endl;
    case 0:
        std::cout << "等待超时" << std::endl;
    default:
        // 意味着select等待成功，判断就绪文件描述符是哪些文件描述符
        for (int i = 0; i < NUM; i++)
        {
            // 判断fd_arry[i]是否在select等待成功的readfds中
            if (FD_ISSET(fd_arry[i], &readfds))
            {
                
                std::cout << "sock: " << fd_arry[i] << " 上面有了读事件，可以读取了" << std::endl;
                // fd_arry[i]在readfds中，是就绪的文件描述符
                // 但是要判断这个fd_farry[i]是listenfd就绪还是普通fd就绪
                if (fd_arry[i] == listenfd)
                {
                    std::cout << "listen_sock: " << listenfd<< " 有了新的链接到来" << std::endl;
                    // listenfd就绪了，就可以开始获取连接了
                    int newfd = Socket::Accept(listenfd);
                    if(newfd>=0)
                    {
                        std::cout << "listen_sock: " << listenfd << " 获取新的链接成功" << std::endl;
                    }

                    // 获取了新连接的文件描述符，但是后续read要进行读取时，还得等待newfd就绪
                    // 所以就让select去等待newfd就绪

                    // 先在fd_arry中找一个空元素
                    int pos=1;
                    for ( pos=1 ; pos < NUM; pos++)
                    {
                        if (fd_arry[pos] == -1)
                        {
                            //找到了空位置
                            break;
                        }
                    }
                    if(pos<NUM)
                    {
                        std::cout<<"已经成功将新连接 " << newfd <<" 加入了fd_arry中,表示其需要被select等待"<<std::endl;
                        fd_arry[pos] = newfd;
                    }
                    else
                    {
                        //pos非法
                        std::cout<<"找到的pos位置非法,服务器满载了，无法获取当前连接"<<std::endl;
                        close(newfd);
                    }

                }
                else
                {
                    // fd_arry[i]是普通文件描述符就绪了，所以可以开始进行数据拷贝，即读取数据
                    char buffer[1024];
                    int s = read(fd_arry[i], buffer, sizeof(buffer) - 1);
                    if (s > 0)
                    {
                        buffer[s] = 0;
                        std::cout << "client: " << buffer << std::endl;
                    }
                    else if (s == 0)
                    {
                        // 对端关闭了连接
                        // 此时就要将当前fd_arry[i]从fd_arry数组中移除，因为对端关闭了连接
                        // 意味着再也不会向fd_arry[i]这个文件描述符的接收缓冲区中发送数据
                        // 也就意味着fd_arry[i]这个文件描述符的接收缓冲区永远为空
                        // 即再也不会就绪，所以等待fd_arry[i]就绪就是毫无意义的，所以不必再等待
                        close(fd_arry[i]);
                        fd_arry[i] = -1;
                   
                    }
                    else
                    {
                        // 读取出错
                        std::cout << " read error!" << std::endl;
                        close(fd_arry[i]);
                        fd_arry[i] = -1;
                    }
                }
            }
        }
    }

    }
   

    return 0;
}
